package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.api.SeckillGoodsService;
import com.atguigu.gmall.activity.util.DateUtil;
import com.atguigu.gmall.common.constant.RabbitConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author: 封捷
 * @create-date: 2022/9/18 15:34
 */
@Component
public class SeckillReceiver {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private SeckillGoodsService seckillGoodsService;

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = RabbitConst.EXCHANGE_DIRECT_TASK),
            value = @Queue(value = RabbitConst.QUEUE_TASK_1),
            key = {RabbitConst.ROUTING_TASK_1}
    ))
    public void importItemToRedis(Message message, Channel channel) throws IOException {
        // 1、查询当天参与秒杀活动的秒杀数据
        // [1]封装查询条件
        // ※查询条件1：status 字段值为 1
        // ※查询条件2：库存大于 0
        // ※查询条件3：开始时间是今天
        // DATE_FORMAT()是SQL中的一个函数，可以格式化日期
        // DateUtil.formatDate()是我们导入工具类中的一个工具方法，业务是格式化日期
        // 都是格式化为年、月、日的形式，方便我们找到今天的秒杀商品
        // 注意：DATE_FORMAT中使用的是底杠，%Y-%m-%d中使用的是中杠
        QueryWrapper<SeckillGoods> queryWrapper = new QueryWrapper<SeckillGoods>()
                .eq("status", 1)
                .gt("stock_count", 0)
                .eq("DATE_FORMAT(start_time, '%Y-%m-%d')",
                        DateUtil.formatDate(new Date()));

        // [2]执行查询
        List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(queryWrapper);

        if (CollectionUtils.isEmpty(seckillGoodsList)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return ;
        }

        for (SeckillGoods seckillGoods : seckillGoodsList) {
            // 2、把商品数据存入 Redis
            // [1]获取 skuId
            Long skuId = seckillGoods.getSkuId();

            // [2]检查当前 skuId 是否已经存入 Redis
            // ※小心有坑：Redis 的 field 是字符串类型，而我们这里 skuId 是 Long 类型，必须统一
            Boolean hasKeyCheckResult = redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).hasKey(skuId.toString());

            if (hasKeyCheckResult) {
                // 如果当前 skuId 已经保存，则跳过本次循环
                continue ;
            }

            // [3]执行保存
            redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).put(skuId.toString(), seckillGoods);

            // 3、把防超卖队列存入 Redis
            // 每一个 skuId 对应一个防超卖队列
            // [1]获取当前商品的库存数量
            Integer stockCount = seckillGoods.getStockCount();

            // [2]创建集合，用于存放防超卖队列数据
            List<Long> skuIdList = new ArrayList<>();

            // [3]按照 stockCount 进行循环
            for (int i = 0; i < stockCount; i++) {
                skuIdList.add(skuId);
            }

            // [4]把防超卖队列存入 Redis
            redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).leftPushAll(skuIdList.toArray());

            // 4、初始化状态位
            redisTemplate.convertAndSend("seckillpush", seckillGoods.getSkuId()+":1");

        }

        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = RabbitConst.EXCHANGE_DIRECT_SECKILL_USER),
            value = @Queue(value = RabbitConst.QUEUE_SECKILL_USER),
            key = {RabbitConst.ROUTING_SECKILL_USER}
    ))
    public void processUserRecodeMessage(UserRecode userRecode, Message message, Channel channel) throws IOException {

        if (userRecode != null) {
            seckillGoodsService.addUserRecodeToQueue(userRecode);
        }

        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

    }

    /**
     * 秒杀结束清空缓存
     *
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = RabbitConst.QUEUE_TASK_18, durable = "true"),
            exchange = @Exchange(value = RabbitConst.EXCHANGE_DIRECT_TASK),
            key = {RabbitConst.ROUTING_TASK_18}
    ))
    public void clearRedis(Message message, Channel channel) throws IOException {

        // 活动结束清空缓存
        QueryWrapper<SeckillGoods> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", 1);
        queryWrapper.le("end_time", new Date());
        List<SeckillGoods> list = seckillGoodsMapper.selectList(queryWrapper);

        // 清空缓存
        for (SeckillGoods seckillGoods : list) {
            redisTemplate.delete(RedisConst.SECKILL_STOCK_PREFIX + seckillGoods.getSkuId());
        }

        redisTemplate.delete(RedisConst.SECKILL_GOODS);
        redisTemplate.delete(RedisConst.SECKILL_ORDERS);
        redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);

        // 将状态更新为结束
        SeckillGoods seckillGoodsUp = new SeckillGoods();
        seckillGoodsUp.setStatus("2");
        seckillGoodsMapper.update(seckillGoodsUp, queryWrapper);

        // 手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

}
